package org.llc.common.exception;

import feign.RetryableException;
import lombok.extern.slf4j.Slf4j;
import org.llc.common.enums.CodeEnum;
import org.llc.common.model.Result;
import org.springframework.http.HttpStatus;
//import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 全局异常处理
 * @author llc
 * @date 2017/9/24 12:42
 * @since 1.0.0
 */
@Slf4j
@ControllerAdvice
@SuppressWarnings("all")
public class GlobalControllerExceptionHandler {

	/**
	 * 系统异常处理
	 * @see ExceptionHandler
	 * @author llc
	 * @date 2019/6/18 9:31
	 * @param  e 异常对象
	 */
//	@ExceptionHandler({ BusinessCommonException.class, Throwable.class })
	@ResponseBody
    public Result errorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
        Result result = Result.failed(CodeEnum.ERROR.getRetCode(),CodeEnum.ERROR.getRetMsg() + e.getMessage());
        log.error(result.toJsonString(),e);
        // 获取请求URI
        this.getRequestURI(request);
        return result;
    }

	/**
	 * 运行时异常处理
	 * @see ExceptionHandler
	 * @author llc
	 * @date 2019/6/18 9:31
	 * @param  e 异常对象
	 */
	@ExceptionHandler(RuntimeException.class)
	@ResponseBody
	public Result runtimeExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
		Result result = Result.failed(CodeEnum.ERROR.getRetCode(),CodeEnum.ERROR.getRetMsg() + e.getMessage());
		log.error(result.toJsonString(),e);
		// 获取请求URI
		this.getRequestURI(request);
		return result;
	}

    /**
     * 请求超时异常处理
     * @see ExceptionHandler
     * @author llc
     * @date 2019/6/18 9:31
     * @param  e 异常对象
     */
    @ExceptionHandler(RetryableException.class)
    @ResponseBody
    public Result retryableExceptionHandler(HttpServletRequest request, RetryableException e) {
        Result result = Result.failed(CodeEnum.REQUEST_TIMED_OUT_ERROR.getRetCode(),CodeEnum.REQUEST_TIMED_OUT_ERROR.getRetMsg() + e.getMessage());
        log.error(result.toJsonString(),e);
        // 获取请求URI
        this.getRequestURI(request);
        return result;
    }

	/**
	 * 参数校验异常
	 * <p> 表单提交时发生的参数绑定一场</p>
	 * @see javax.validation.constraints.NotBlank
	 * @see javax.validation.constraints.NotNull
     * @date 2019/7/18 9:32
     * @author llc
     * @param e 异常对象
     * @return Result   返回类型
     * @throws
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result errorBindHandler(BindException e) {
        log.error("BindException -> 参数校验异常", e);
        return wrapperBindingResult(e.getBindingResult());
    }

	/**
	 * 参数校验(Valid)异常，将校验失败的所有异常组合成一条错误信息
	 *
	 * <p> 表单提交时Content-Type为 “application / x-www-form-urlencoded”。
	 * 因此，Spring将数据解释为Web表单数据（而不是JSON）。 Spring使用FormHttpMessageConverter将POST主体转换为域对象，并导致BindException。
	 * 我们想要的是Spring将POST数据视为JSON，并使用MappingJackson2HttpMessageConverter将POST正文解析为对象。
	 * </p>
	 *
	 * @author llc
	 * @date 2019/6/18 9:31
	 * @param e 异常对象
	 * @return 异常结果
	 */
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	@ResponseBody
	public Result validExceptionHandler(HttpServletRequest request,MethodArgumentNotValidException e) {
		log.error("MethodArgumentNotValidException -> 参数绑定校验异常", e);
		// 获取请求URI
		this.getRequestURI(request);
		return wrapperBindingResult(e.getBindingResult());
	}

	/**
	 * 系统自定义异常处理
	 * @author llc
	 * @date 2019/6/18 12:42
	 * @param  e 异常对象
	 * @return BaseHttpParamsResp    返回类型
	 */
	@ExceptionHandler(BusinessException.class)
	@ResponseBody
	public Result customErrorHandler(HttpServletRequest request, HttpServletResponse response, BusinessException e) {
		Result result = Result.failed(CodeEnum.ERROR.getRetCode(), e.getLocalizedMessage());
		log.error(result.toJsonString(),e);
		// 获取请求URI
		this.getRequestURI(request);
		return result;
	}

	/**
	 * 微服务间调用返回异常给上级
	 * @author llc
	 * @date 2019/6/26 16:08
	 * @param request 请求
	 * @param response 响应
	 * @param e  异常对象
	 */
	@ExceptionHandler(MicroServiceCallException.class)
	@ResponseBody
	public Result microServiceCallErrorHandler(HttpServletRequest request, HttpServletResponse response, MicroServiceCallException e) {
		Result result = Result.failed(e.getErrorCode(),e.getLocalizedMessage());
		log.error(result.toJsonString(),e);
		// 获取请求URI
		this.getRequestURI(request);
		return result;
	}

	/**
	 * 系统自定义异常处理
	 * @param e 异常对象
	 * @return org.llc.common.model.Result
	 * @author llc
	 * @date 2020/5/15 14:25
	 */
	@ExceptionHandler(BusinessCommonException.class)
	@ResponseBody
	public Result businessCommonExceptionHandler(HttpServletRequest request, HttpServletResponse response, BusinessCommonException e) {
		Result result = Result.failed(e.getErrorCode(), e.getAdditionMessage());
		log.error(result.toJsonString(),e);
		// 获取请求URI
		this.getRequestURI(request);
		return result;
	}

	/**
	 * IllegalArgumentException（非法参数）异常处理返回json 状态码:400
	 * @author llc
	 * @date 2019/6/24 14:10
	 * @param exception 异常对象
	 * @return com.bccv.nmg.result.Result
	 */
	@ExceptionHandler({ IllegalArgumentException.class })
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public Result badRequestExceptionHandler(HttpServletRequest request,IllegalArgumentException exception) {
		log.error("IllegalArgumentException -> 400",exception);
		// 获取请求URI
		this.getRequestURI(request);
		return Result.failed(HttpStatus.BAD_REQUEST.value(),exception.getMessage());
	}


	/**
	 * AccessDeniedException异常处理返回json 状态码:401 403
	 * @author llc
	 * @Description
	 * @date 2019/6/24 14:10
	 * @param exception 异常对象
	 * @return com.bccv.nmg.result.Result
	 */
	@ExceptionHandler({ Exception.class })
	public void badMethodExpressExceptionHandler(HttpServletRequest request,Exception exception) throws Exception {
		log.error("AccessDeniedException -> 403",exception);
		// 获取请求URI
		this.getRequestURI(request);
		throw exception;
	}


	/**
	 * 获取请求URI
	 * @param request
	 * @return void
	 * @author llc
	 * @date 2020/5/2 21:32
	 */
	private void getRequestURI(HttpServletRequest request){
		// 获取请求路径
		String requestUrl = request.getRequestURI();
		log.error("获取请求URI -> {}",requestUrl);
	}

	/**
	 * 包装绑定异常结果
	 * @author llc
	 * @date 2019/6/18 9:31
	 * @param bindingResult 绑定结果
	 * @return Result 异常结果
	 */
	private Result wrapperBindingResult(BindingResult bindingResult) {
		// 构建返回消息
		StringBuilder msg = new StringBuilder();
		for (ObjectError error : bindingResult.getAllErrors()) {
			msg.append(", ");
			if (error instanceof FieldError) {
				msg.append(((FieldError) error).getField()).append(": ");
			}
			msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
		}
		return Result.failed(CodeEnum.ERROR.getRetCode(), msg.substring(2));
	}
}
